package cz.muni.fi.pa165.machineryrental.dao.impl;

import cz.muni.fi.pa165.machineryrental.dao.MachineDAO;
import cz.muni.fi.pa165.machineryrental.entities.Machine;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * Implementation of MachineDAO which is used on persisting and other operations
 * on machines in database.
 *
 * @author Martin Klvana, 395893
 */
@Repository
@Component
public class MachineDAOImpl implements MachineDAO {

    /**
     * Entity Manager for performing operations in or from database.
     */
    @PersistenceContext(unitName = "ClassicPU")
    private EntityManager em;

    /**
     * Creates new instance of MachineDAOImpl on performing operations on
     * entities into or from database.
     *
     * @param em Entity manager running the operations to database.
     */
    public void setEntityManager(EntityManager em) {
        if (em == null) {
            throw new DataAccessException("Given Entity manager cannot be null.") {
            };
        }

        this.em = em;
    }

    /**
     * @see cz.muni.fi.pa165.machineryrental.dao.MachineDAO#getById(long)
     */
    @Override
    public Machine getById(long id) {

        if (id < 0) {
            throw new DataAccessException("ID cannot be negative") {
            };
        }

        return em.find(Machine.class, id);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#create(cz.muni.fi.pa165.machineryrental.entities.Machine)
     */
    @Override
    public void create(Machine machine) {
        if (machine == null) {
            throw new DataAccessException("Machine cannot be null") {
            };
        }

        this.em.persist(machine);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#update(cz.muni.fi.pa165.machineryrental.entities.Machine)
     */
    @Override
    public void update(Machine machine) {
        if (machine == null) {
            throw new DataAccessException("Machine cannot be null") {
            };
        }

        this.em.merge(machine);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#remove(cz.muni.fi.pa165.machineryrental.entities.Machine)
     */
    @Override
    public void remove(Machine machine) {
        if (machine == null) {
            throw new DataAccessException("Machine cannot be null") {
            };
        }

        Machine setToFind = em.find(Machine.class, machine.getId());
        if (setToFind == null) {
            throw new PersistenceException("Machine is not in database");
        }
        try {
            em.remove(setToFind);
            em.flush();
        } catch (IllegalArgumentException ex) {
            throw new PersistenceException(ex);
        }
    }

    /**
     * @see cz.muni.fi.pa165.machineryrental.dao.MachineDAO#findAll()
     */
    @Override
    public List<Machine> findAll() {
        TypedQuery<Machine> query = this.em.createQuery("FROM Machine M", Machine.class);
        return query.getResultList();
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#findByCode(java.lang.String)
     */
    @Override
    public Machine findByCode(String code) {

        if (code == null) {
            throw new DataAccessException("Code cannot be null!") {
            };
        }

        TypedQuery<Machine> query = em.createQuery("SELECT c FROM Machine c WHERE c.code = :code",
                Machine.class).setParameter("code", code);
        List<Machine> result = query.getResultList();
        return result.size() > 0 ? result.get(0) : null;
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#findByBrand(java.lang.String)
     */
    public List<Machine> findByBrand(String brand) {

        if (brand == null) {
            throw new DataAccessException("Brand cannot be null!") {
            };
        }

        TypedQuery<Machine> query = em.createQuery("SELECT c FROM Machine c WHERE c.brand = :brand",
                Machine.class).setParameter("brand", brand);
        return query.getResultList();
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.MachineDAO#findByModel(java.lang.String)
     */
    public List<Machine> findByModel(String model) {

        if (model == null) {
            throw new DataAccessException("Model cannot be null!") {
            };
        }

        TypedQuery<Machine> query = em.createQuery("SELECT c FROM Machine c WHERE c.model = :model",
                Machine.class).setParameter("model", model);
        return query.getResultList();
    }

    /**
     * Checks if given code of machine exists. If not, than return true, else
     * return false.
     *
     * @param code New code of machine
     * @return True if is unique, else false.
     */
    @Override
    public boolean isCodeUnique(String code) {

        if (code == null) {
            throw new DataAccessException("Code cannot be null!") {
            };
        }

        TypedQuery<Machine> query = em.createQuery("SELECT c FROM Machine c WHERE c.code = :code",
                Machine.class).setParameter("code", code);
        return query.getResultList().isEmpty();
    }
}
